<?php
namespace restphp\utils;

use restphp\biz\RestErrorCode;
use restphp\exception\RestException;

/**
 * Class RestClassUtils
 * @package restphp\utils
 */
class RestClassUtils {
    private static $__DATA_TYPE = array(
        'int',
        'integer',
        'float',
        'double',
        'string',
        'false',
        'true',
        'boolean',
        'bool',
        'array',
        '*'
    );

    /**
     * 从数组对象中复制对象.
     * @param $object object
     * @param $arrSource
     * @param null $def
     * @return object $object
     */
    public static function copyFromArr($object, $arrSource, $def = null) {
        if (gettype($object) == 'string') {
            $object = new $object();
        }
        $objectInvoke = new \ReflectionObject($object);
        $arrProperties = $objectInvoke->getProperties();
        $arrMethodsSource = $objectInvoke->getMethods();
        $arrMethods = array();
        foreach ($arrMethodsSource as $method) {
            $arrMethods[] = $method->getName();
        }
        $bMatch = false;
        foreach ($arrProperties as $property) {
            $column = $property->getName();
            if ('_' == substr($column, 0, 1)) {
                $column = substr($column, 1);
            }
            $columnKey = self::_analysisJsonNameRemark($column, $property->getDocComment());
            $setFuncName = 'set' . strtoupper(substr($column, 0, 1)) . substr($column, 1);
            if (isset($arrSource[$columnKey]) && in_array($setFuncName, $arrMethods)) {
                $mxVal = $arrSource[$columnKey];
                $var = self::_analysisVar($property->getDocComment());
                if (!in_array($var, self::$__DATA_TYPE)) {
                    if (RestStringUtils::startWith($var,'array<')) {
                        $haystackLen = strlen('array<');
                        $subVar = substr($var, $haystackLen, strlen($var) - $haystackLen - 1);
                        $arrNewVal = array();
                        foreach ($mxVal as $valItem) {
                            if (!in_array($subVar, self::$__DATA_TYPE)) {
                                $arrNewVal[] = self::copyFromArr($subVar, $valItem);
                            } else {
                                $arrNewVal[] = $valItem;
                            }
                        }
                        $mxVal = $arrNewVal;
                    } else {
                        $mxVal = self::copyFromArr($var, $mxVal);
                    }
                } else {
                    if ('int' == $var || 'integer' == $var) {
                        $mxVal = intval($mxVal);
                    } else if ('float' == $var) {
                        $mxVal = floatval($mxVal);
                    } else if ('double' == $var) {
                        $mxVal = doubleval($mxVal);
                    } else if ('string' == $var) {
                        $mxVal = strval($mxVal);
                    } else if ('false' == $var) {
                        $mxVal = false;
                    } else if ('true' == $var) {
                        $mxVal = true;
                    } else if ('boolean' == $var || 'bool' == $var) {
                        $mxVal = boolval($mxVal);
                    }
                }
                $object->$setFuncName($mxVal);
                $bMatch = true;
            }
        }
        if (!$bMatch) {
            return $def;
        }
        return $object;
    }

    /**
     * json字段别名识别.
     * @param $column
     * @param $doc
     * @return string
     */
    private static function _analysisJsonNameRemark($column, $doc) {
        if (RestStringUtils::isBlank($doc)) {
            return $column;
        }
        $arrDoc = explode("\n", $doc);
        foreach ($arrDoc as $strLine) {
            //if (!mb_strpos($strLine, "@alias(")) {
            //    continue;
            //}
            if (!preg_match('/@alias\\s*\(/', $strLine)) {
                continue;
            }

            $start = mb_strpos($strLine, "(") + strlen("(");
            $end = mb_strpos($strLine, ")", $start);
            $colVal = mb_substr($strLine, $start, $end - $start);
            $colVal = str_replace("value", "", $colVal);
            $colVal = str_replace("=", "", $colVal);
            $colVal = str_replace("\"", "", $colVal);
            $colVal = trim($colVal);
            if (!RestStringUtils::isBlank($colVal)) {
                $column = $colVal;
            }
        }
        return $column;
    }

    /**
     * 获取数据类型定义.
     * @param $doc
     * @return mixed|string|null
     */
    private static function _analysisVar($doc) {
        if (RestStringUtils::isBlank($doc)) {
            return "*";
        }
        $arrDoc = explode("\n", $doc);
        $var = null;
        foreach ($arrDoc as $strLine) {
            if (!mb_strpos($strLine, "@var ")) {
                continue;
            }

            $content = substr($strLine, mb_strpos($strLine,"@var ") + strlen("@var "));
            $arrContent = explode(" ", trim($content));
            if (isset($arrContent[0])) {
                $var = trim($arrContent[0]);
            }
        }
        if (RestStringUtils::isBlank($var)) {
            $var = "*";
        }
        return $var;
    }

    /**
     * 对象复制.
     * @param $object
     * @param $source
     * @param bool $skipNull
     * @return object|null
     */
    public static function copy($object, $source, $skipNull = true) {
        $arrSource = self::beanToArr($source, $skipNull);
        return self::copyFromArr($object, $arrSource);
    }

    /**
     * class to array.
     * @param $object
     * @return array
     */
    public static function beanToArr($object, $skipNull = true) {
        if (gettype($object) == 'string') {
            $object = new $object();
        }
        $objectInvoke = new \ReflectionObject($object);
        $arrProperties = $objectInvoke->getProperties();

        $arrData = array();

        foreach ($arrProperties as $property) {
            $column = $property->getName();
            if ('_' == substr($column, 0, 1)) {
                $column = substr($column, 1);
            }

            $getFuncName = 'get' . strtoupper(substr($column, 0, 1)) . substr($column, 1);

            $columnToKey = self::_analysisJsonNameRemark($column, $property->getDocComment());

            $mixValue = null;

            try {
                $mixValue = $object->$getFuncName();
            } catch (\Exception $e) {
                throw new RestException(RestErrorCode::CLASS_BEAN_TO_ARR_GET_VAL_EXCEPTION);
            }

            if ($skipNull && !is_numeric($mixValue) && null == $mixValue) {
                continue;
            }

            $arrData[$columnToKey] = $mixValue;
        }

        //子集中的对象转数组
        $arrData = self::subBeanToArr($arrData, $skipNull);

        return $arrData;
    }

    /**
     * 子集中的对象转数组.
     * @param $arrSource
     * @param $object
     * @param bool $skipNull
     * @return mixed
     */
    public static function subBeanToArr($arrSource, $skipNull = true) {
        foreach ($arrSource as $key=>&$value) {
            if (is_array($value)) {
                $value = self::subBeanToArr($value, $skipNull);
            } else if (is_object($value)) {
                $value = self::beanToArr($value, $skipNull);
            }
        }
        return $arrSource;
    }

    /**
     * 对象列表转二维数组.
     * @param array $arrObjects
     * @param bool $skipNull
     * @return array
     */
    public static function beansToArrayList(array $arrObjects, $skipNull = true) {
        $arrList = array();
        foreach ($arrObjects as $object) {
            $arrList[] = self::beanToArr($object, $skipNull);
        }

        return $arrList;
    }

    /**
     * class bean to json string.
     * @param $object
     * @return string
     */
    public static function beanToJsonString($object) {
        $arrBean = self::beanToArr($object);
        return json_encode($arrBean);
    }

    /**
     * string to bean object.
     * @param $strSource
     * @param $oObject
     * @return object
     */
    public static function parseObject($strSource, $oObject) {
        $arr = json_decode($strSource, true);
        if (empty($arr)) {
            return null;
        }
        return self::copyFromArr($oObject, $arr);
    }
}